package com.smedi.ismedi.file.core.infrastructure.FileConvert;

import cn.hutool.core.util.IdUtil;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.smedi.ismedi.file.core.domain.aggregatemodel.FileInfo;
import com.smedi.ismedi.file.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.web.starter.exception.LogicException;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.docx4j.Docx4J;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Slf4j
@Service
@AllArgsConstructor
public class LibreOfficeUtil {
    private final FastFileStorageClient storageClient;

    @SneakyThrows
    public FileInfo convertToPdf(String inputFileName, String inputFileExtName, byte[] bytes) {
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + inputFileName + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, "." + inputFileExtName);

        // 输入字节流转为File
        try (FileOutputStream fos = new FileOutputStream(inputFile)) {
            fos.write(bytes, 0, bytes.length);
        } catch (Exception e) {
            throw LogicException.raise(CustomError.FILE_WRITE_FAIL);
        }

        return convertPdf(inputFile);
    }

    @SneakyThrows
    public FileInfo convertToPdf(MultipartFile inputMultipartFile) {
        // inputMultipartFile转为inputFile
        // 获取文件名
        String inputFileName = inputMultipartFile.getOriginalFilename();
        // 获取文件简称和后缀
        assert inputFileName != null;
        String shortname = inputFileName.substring(0, inputFileName.lastIndexOf("."));
        String prefix = inputFileName.substring(inputFileName.lastIndexOf("."));
        // 用uuid作为文件名，防止生成的临时文件重复
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + shortname + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, prefix);
        // MultipartFile to File
        inputMultipartFile.transferTo(inputFile);

        return convertPdf(inputFile);
    }

    @SneakyThrows
    public FileInfo convertToPdfNoSign(String inputFileName, String inputFileExtName, byte[] bytes) {
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + inputFileName + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, "." + inputFileExtName);

        // 输入字节流转为File
        try (FileOutputStream fos = new FileOutputStream(inputFile)) {
            fos.write(bytes, 0, bytes.length);
        } catch (Exception e) {
            throw LogicException.raise(CustomError.FILE_WRITE_FAIL);
        }

        return convertToPdf(inputFile);
    }

    @SneakyThrows
    public FileInfo convertToPdfNoSign(MultipartFile inputMultipartFile) {
        // inputMultipartFile转为inputFile
        // 获取文件名
        String inputFileName = inputMultipartFile.getOriginalFilename();
        // 获取文件简称和后缀
        assert inputFileName != null;
        String shortname = inputFileName.substring(0, inputFileName.lastIndexOf("."));
        String prefix = inputFileName.substring(inputFileName.lastIndexOf("."));
        // 用uuid作为文件名，防止生成的临时文件重复
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + shortname + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, prefix);
        // MultipartFile to File
        inputMultipartFile.transferTo(inputFile);

        return convertToPdf(inputFile);
    }

    @SneakyThrows
    public FileInfo convertToPdfNoFree(String inputFileName, String inputFileExtName, byte[] bytes) {
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + inputFileName + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, "." + inputFileExtName);

        // 输入字节流转为File
        try (FileOutputStream fos = new FileOutputStream(inputFile)) {
            fos.write(bytes, 0, bytes.length);
        } catch (Exception e) {
            throw LogicException.raise(CustomError.FILE_WRITE_FAIL);
        }

        return convertPdfNoFree(inputFile);
    }

    @SneakyThrows
    public FileInfo convertToPdfNoFree(MultipartFile inputMultipartFile) {
        // inputMultipartFile转为inputFile
        // 获取文件名
        String inputFileName = inputMultipartFile.getOriginalFilename();
        // 获取文件简称和后缀
        assert inputFileName != null;
        String shortname = inputFileName.substring(0, inputFileName.lastIndexOf("."));
        String prefix = inputFileName.substring(inputFileName.lastIndexOf("."));
        // 用uuid作为文件名，防止生成的临时文件重复
        String outputFile = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + shortname + UUID.randomUUID();
        final File inputFile = File.createTempFile(outputFile, prefix);
        // MultipartFile to File
        inputMultipartFile.transferTo(inputFile);

        return convertPdfNoFree(inputFile);
    }

    @SneakyThrows
    private synchronized FileInfo convertToPdf(File inputFile) {
        Runtime runtime = Runtime.getRuntime();
        Process process;
        StorePath storePath;

        // 获取文件路径和文件名
        String shortName = inputFile.getName().substring(0, inputFile.getName().lastIndexOf("."));
        String outputFileName = shortName + ".pdf";
        String inputFileAbsolutePath = inputFile.getAbsolutePath();
        Path inputFilePath = Paths.get(inputFileAbsolutePath);
        String outputFileAbsolutePath = inputFileAbsolutePath.substring(0, inputFileAbsolutePath.lastIndexOf(".")) + ".pdf";
        Path outputFilePath = Paths.get(outputFileAbsolutePath);

        try {
            // 执行linux命令转换文件
            log.debug("要更改的inputFile路径:{}", inputFileAbsolutePath);
            String command = "unoconv -f pdf " + inputFileAbsolutePath;
            log.debug("要执行的命令:{}", command);
            process = runtime.exec(command);
            log.debug("执行命令:{}，inputFile的size:{}", command, inputFile.length());

            process.waitFor();
            log.debug("命令等待");
            process.destroy();
            log.debug("命令销毁");
            File file = new File(outputFilePath.toString());
            log.debug("outputFile文件名:{},outputFile路径:{},outputFile的size:{}", file.getName(), file.getAbsolutePath(), file.length());
            try (InputStream inputStream = new FileInputStream(file)) {
                storePath = storageClient.uploadFile(inputStream, file.length(), ".pdf", null);
                log.debug("上传成功{}", file.getName());
            }
        } catch (Exception e) {
            // 会导致临时文件无法删除
            log.error("文件转换失败", e);
            Thread.currentThread().interrupt();
            throw LogicException.raise(CustomError.FILE_CONVERT_FAIL);
        } finally {
            Files.deleteIfExists(inputFilePath);
            Files.deleteIfExists(outputFilePath);
        }

        if (null == storePath) {
            throw LogicException.raise(CustomError.FILE_UPLOAD_ERROR);
        }

        return FileInfo.builder()
                .extName(".pdf")
                .name(outputFileName)
                .signKey(IdUtil.simpleUUID())
                .path(storePath.getPath())
                .storageGroup(storePath.getGroup())
                .build();
    }

    @SneakyThrows
    private FileInfo convertPdf(File inputFile) {
        StorePath storePath = new StorePath();

        // 获取文件路径和文件名
        String shortName = inputFile.getName().substring(0, inputFile.getName().lastIndexOf("."));
        String outputFileName = shortName + ".pdf";
        String inputFileAbsolutePath = inputFile.getAbsolutePath();
        Path inputFilePath = Paths.get(inputFileAbsolutePath);
        String outputFileAbsolutePath = inputFileAbsolutePath.substring(0, inputFileAbsolutePath.lastIndexOf(".")) + ".pdf";
        Path outputFilePath = Paths.get(outputFileAbsolutePath);

        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFileAbsolutePath)) {

            WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(inputFile);
            Mapper fontMapper = new IdentityPlusMapper();
            fontMapper.put("SimSun", PhysicalFonts.get("Songti TC Bold"));
            mlPackage.setFontMapper(fontMapper);
            // 转 PDF
            Docx4J.toPDF(mlPackage, fileOutputStream);
            File file = new File(outputFilePath.toString());
            try (InputStream inputStream = new FileInputStream(file)) {
                storePath = storageClient.uploadFile(inputStream, file.length(), ".pdf", null);
                log.debug("上传成功{}", file.getName());
            }
        } catch (Exception e) {
            // 会导致临时文件无法删除
            Thread.currentThread().interrupt();
            throw LogicException.raise(CustomError.FILE_CONVERT_FAIL);
        } finally {
            Files.deleteIfExists(inputFilePath);
            Files.deleteIfExists(outputFilePath);
        }

        if (null == storePath.getPath()) {
            throw LogicException.raise(CustomError.FILE_UPLOAD_ERROR);
        }

        return FileInfo.builder()
                .extName(".pdf")
                .name(outputFileName)
                .signKey(IdUtil.simpleUUID())
                .path(storePath.getPath())
                .storageGroup(storePath.getGroup())
                .build();
    }

    @SneakyThrows
    private FileInfo convertPdfNoFree(File inputFile) {
        StorePath storePath = new StorePath();

        // 获取文件路径和文件名
        String shortName = inputFile.getName().substring(0, inputFile.getName().lastIndexOf("."));
        String outputFileName = shortName + ".pdf";
        String inputFileAbsolutePath = inputFile.getAbsolutePath();
        Path inputFilePath = Paths.get(inputFileAbsolutePath);
        String outputFileAbsolutePath = inputFileAbsolutePath.substring(0, inputFileAbsolutePath.lastIndexOf(".")) + ".pdf";
        Path outputFilePath = Paths.get(outputFileAbsolutePath);

        try {
            //实例化Document类的对象
            Document doc = new Document();

            //加载Word
            doc.loadFromFile(inputFile.getAbsolutePath());

            //保存为PDF格式
            doc.saveToFile(outputFilePath.toString(), FileFormat.PDF);
            File file = new File(outputFilePath.toString());
            try (InputStream inputStream = new FileInputStream(file)) {
                storePath = storageClient.uploadFile(inputStream, file.length(), ".pdf", null);
                log.debug("上传成功{}", file.getName());
            }
        } catch (Exception e) {
            // 会导致临时文件无法删除
            Thread.currentThread().interrupt();
            throw LogicException.raise(CustomError.FILE_CONVERT_FAIL);
        } finally {
            Files.deleteIfExists(inputFilePath);
            Files.deleteIfExists(outputFilePath);
        }

        if (null == storePath.getPath()) {
            throw LogicException.raise(CustomError.FILE_UPLOAD_ERROR);
        }

        return FileInfo.builder()
                .extName(".pdf")
                .name(outputFileName)
                .signKey(IdUtil.simpleUUID())
                .path(storePath.getPath())
                .storageGroup(storePath.getGroup())
                .build();
    }
}
